package tech.summerly.weather.data.remote

import android.support.annotation.VisibleForTesting
import com.jakewharton.retrofit2.adapter.kotlin.coroutines.experimental.CoroutineCallAdapterFactory
import kotlinx.coroutines.experimental.Deferred
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import tech.summerly.weather.AppContext
import tech.summerly.weather.R
import tech.summerly.weather.data.bean.Forecast
import tech.summerly.weather.data.bean.RealTime
import tech.summerly.weather.data.bean.WeatherResponse
import tech.summerly.weather.util.Position
import tech.summerly.weather.util.log
import java.io.IOException

/**
 * Created by summer on 18-3-7
 */
object CaiYunService {

    private const val TOKEN = "MgDzNUuPrm1HVpvI"

    private val context get() = AppContext.instance

    @VisibleForTesting
    val service: CaiYunServiceApi

    init {
        service = Retrofit.Builder()
                .baseUrl("https://api.caiyunapp.com/v2/$TOKEN/")
                .callFactory(OkHttpClient.Builder().addInterceptor(HttpLoggingInterceptor()).build())
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(CoroutineCallAdapterFactory())
                .build()
                .create(CaiYunServiceApi::class.java)
    }


    /**
     * @return 实时天气信息
     */
    suspend fun getRealtimeWeather(position: Position): RealTime {
        log { "get realtime weather from net $position " }
        return getWeatherWithPeat { service.getRealTime(position.stringify()) }
    }


    /**
     * @return 天气预报信息
     */
    suspend fun getForecastWeather(position: Position): Forecast {
        log { "get forecast weather from net $position " }
        return getWeatherWithPeat { service.getForecast(position.stringify()) }
    }


    @Throws(IOException::class)
    private suspend fun <T> getWeatherWithPeat(api: () -> Deferred<WeatherResponse<T>>): T {
        repeat(3) {
            //应该不需要加入退避操作吧？？
            try {
                val response = api().await()
                if (!"ok".equals(response.status, true)) {
                    throw IOException(response.error)
                }
                return response.result
            } catch (e: IOException) {
                log { e.printStackTrace();"第 $it 次访问彩云api失败" }
            }
        }
        throw IOException("网络不可用")
    }


    enum class SkyCon {
        /**
         * 晴天
         */
        CLEAR_DAY,
        /**
         * 晴夜
         */
        CLEAR_NIGHT,
        /**
         * 多云
         */
        PARTLY_CLOUDY_DAY,
        /**
         * 多云
         */
        PARTLY_CLOUDY_NIGHT,
        /**
         * 阴
         */
        CLOUDY,
        /**
         * 雨
         */
        RAIN,
        /**
         * 雪
         */
        SNOW,
        /**
         * 风
         */
        WIND,
        /**
         * 雾
         */
        FOG,

        //未知
        UNKNOWN;

        companion object {

            operator fun invoke(skyCon: String): SkyCon {
                return SkyCon.values().find { it.equals(skyCon) } ?: UNKNOWN
            }

            fun getSkyConLocalString(skyCon: String): String {
                return when {
                    SkyCon.CLEAR_DAY.equals(skyCon) -> context.getString(R.string.clear_day)
                    SkyCon.CLEAR_NIGHT.equals(skyCon) -> context.getString(R.string.clear_night)
                    SkyCon.PARTLY_CLOUDY_DAY.equals(skyCon) -> context.getString(R.string.partly_cloudy_day)
                    SkyCon.PARTLY_CLOUDY_NIGHT.equals(skyCon) -> context.getString(R.string.partly_cloudy_night)
                    SkyCon.CLOUDY.equals(skyCon) -> context.getString(R.string.cloudy)
                    SkyCon.RAIN.equals(skyCon) -> context.getString(R.string.rain)
                    SkyCon.SNOW.equals(skyCon) -> context.getString(R.string.snow)
                    SkyCon.WIND.equals(skyCon) -> context.getString(R.string.wind)
                    SkyCon.FOG.equals(skyCon) -> context.getString(R.string.fog)
                    else -> skyCon
                }
            }

            /**
             * @return drawable resource for [skyCon]
             */
            fun getSkyIcon(skyCon: String): Int {
                return when {
                    SkyCon.CLEAR_DAY.equals(skyCon) -> R.drawable.clear
                    SkyCon.CLEAR_NIGHT.equals(skyCon) -> R.drawable.clear_night
                    SkyCon.PARTLY_CLOUDY_DAY.equals(skyCon) -> R.drawable.partly_cloudy
                    SkyCon.PARTLY_CLOUDY_NIGHT.equals(skyCon) -> R.drawable.partly_cloudy_night
                    SkyCon.CLOUDY.equals(skyCon) -> R.drawable.cloudy
                    SkyCon.RAIN.equals(skyCon) -> R.drawable.rain
                    SkyCon.SNOW.equals(skyCon) -> R.drawable.snow
                    SkyCon.WIND.equals(skyCon) -> R.drawable.windy
                    SkyCon.FOG.equals(skyCon) -> R.drawable.foggy
                    else -> R.drawable.sky_na
                }
            }
        }

        fun equals(skyCon: String): Boolean {
            return this.name.equals(skyCon, true)
        }
    }
}